#!/bin/bash

me="${0##*/}"

# convert number of mask bits to x.x.x.x mask format
cidr2mask() {
	local i mask=""
	local full_octets=$(($1/8))
	local partial_octet=$(($1%8))

	for ((i=0;i<4;i+=1)); do
		if [ $i -lt $full_octets ]; then
			mask+=255
		elif [ $i -eq $full_octets ]; then
			mask+=$((256 - 2**(8-$partial_octet)))
		else
			mask+=0
		fi
		test $i -lt 3 && mask+=.
	done

	echo $mask
}

# apply netmask (second argument) to ip address (first argument)
netcalc() {
	local ipa=$(echo ${1} | awk -F. '{ print $1 }')
	local ipb=$(echo ${1} | awk -F. '{ print $2 }')
	local ipc=$(echo ${1} | awk -F. '{ print $3 }')
	local ipd=$(echo ${1} | awk -F. '{ print $4 }')
	local mka=$(echo ${2} | awk -F. '{ print $1 }')
	local mkb=$(echo ${2} | awk -F. '{ print $2 }')
	local mkc=$(echo ${2} | awk -F. '{ print $3 }')
	local mkd=$(echo ${2} | awk -F. '{ print $4 }')
	local nta="$(( $ipa & $mka ))"
	local ntb="$(( $ipb & $mkb ))"
	local ntc="$(( $ipc & $mkc ))"
	local ntd="$(( $ipd & $mkd ))"
	echo "$nta.$ntb.$ntc.$ntd"
}

# Check if the user wants to skip setting the routes
checkskipcmd=$(cat /sys/module/ksocklnd/parameters/skip_mr_route_setup 2>&-)
if [ "$checkskipcmd" == "1" ]; then
	exit 0
fi

# Extract comma-separated interfaces from the argument
j=0
declare -a interfaces
for i in $(echo $1 | sed "s/,/ /g")
do
	# verify that the interface exists
	#echo "$i"
	addr=$(/sbin/ip -o -4 addr list $i 2>&- | awk '{print $4}' | cut -d/ -f1)
	linelen=$(echo -n $addr | wc -m)
	if [[ $linelen -eq 0 ]]; then
		# there's a problem with this interface, skip it
		#echo 'bad!'
		continue
	fi
	# check if route is already set up for this interface
	intfroute=$(/sbin/ip route show table $i 2>&-)
	if [[ ! -z $intfroute ]]; then
		# route exists so skip this interface
		logcmd=(logger "${me}: skip setting up route for ${i}: don\'t overwrite existing route")
		eval "${logcmd[@]}"
		continue
	fi
	interfaces[$j]=$i
	j=$((j+1))
done

# this array will contain the interfaces
# already listed in rt_tables
interfaces_listed=()

# flush cache for every interface
for i in "${interfaces[@]}"
do
	# build command
	redirect="2>&-"
	flushcmd=(/sbin/ip route flush table ${i} ${redirect} )
	# execute command
	eval "${flushcmd[@]}"
	logcmd=(logger "${me}: ${flushcmd[@]}")
	eval "${logcmd[@]}"
done

filename='/etc/iproute2/rt_tables'
n=1
max_table_num=0
while read line; do
	# reading each line
	# trim leading and trailing spaces
	line=`echo $line | sed -e 's/^[[:space:]]*//'`
	linelen=$(echo -n $line | wc -m)
	# don't check empty lines
	if [ $linelen -lt 1 ]; then
		continue
	fi
	# don't check comments
	if [[ ${line:0:1} == "#" ]]; then
		continue
	fi
	# split using space as separator
        splitline=( $line )
	# check the table number and update the max
	if [ $max_table_num -lt ${splitline[0]} ]; then
		max_table_num=${splitline[0]}
	fi
	# check if any of the interfaces are listed
	for i in "${interfaces[@]}"
	do
		if [[ " ${splitline[@]} " =~ " ${i} " ]]; then
			if [[ " ${interfaces[@]} " =~ " ${i} " ]]; then
				interfaces_listed+=($i)
			fi
		fi
	done
	#echo "Line No. $n : $line: $max_table_num"
	n=$((n+1))
done < $filename

# add entries for unlisted interfaces
for i in "${interfaces[@]}"
do
	if [[ ! " ${interfaces_listed[@]} " =~ " ${i} " ]]; then
		max_table_num=$((max_table_num+1))
		echo "$max_table_num $i" >> $filename
	fi
done

# generate list of available default gateways
gwsline=$(/sbin/ip route | awk '/default/ { print $3 }')
gateways=($gwsline)

# select a gateway on the same subnet
selectgw() {
	for gw in "${gateways[@]}"; do
		if [[ "$(netcalc "${1}" "${2}")" == "$(netcalc "${gw}" "${2}")" ]]; then
			echo $gw
			return
		fi
	done
	echo "0.0.0.0"
}

# add the routing entries and rules
for i in "${interfaces[@]}"
do
	# extract ipv4 address and netmask in cidr format
	addr=($(/sbin/ip -o -4 addr list $i 2>&- | awk '{print $4}' | cut -d/ -f1))
	cidrmask=($(/sbin/ip -o -4 addr list $i 2>&- | awk '{print $4}' | cut -d/ -f2))
	# convert cidr mask to mask in dot format
	dotmask=$(cidr2mask ${cidrmask[0]})
	# find a gateway on the same subnet
	gw=$(selectgw ${addr[0]} $dotmask)
	# build and execute route commands
	if [[ $gw == "0.0.0.0" ]]; then
		# gateway not found, assume local destinations
		net=$(netcalc ${addr[0]} $dotmask)
		routecmd=(/sbin/ip route add ${net}/${cidrmask[0]} dev ${i} proto kernel scope link src ${addr[0]} table ${i})
	else
		routecmd=(/sbin/ip route add default via ${gw} dev ${i} table ${i})
	fi
	ruledelcmd=(/sbin/ip rule del from ${addr[0]} table ${i} '&>/dev/null')
	ruleaddcmd=(/sbin/ip rule add from ${addr[0]} table ${i})
	routeerr=$(eval ${routecmd[@]} 2>&1 >/dev/null)
	ruledelerr=$(eval ${ruledelcmd[@]} 2>&1 >/dev/null)
	ruleaddcmd=$(eval ${ruleaddcmd[@]} 2>&1 >/dev/null)
	logcmd1=(logger "${me}: ${routecmd[@]} ${routeerr}")
	logcmd2=(logger "${me}: ${ruledelcmd[@]} ${ruledelerr}")
	logcmd3=(logger "${me}: ${ruleaddcmd[@]} ${ruleaddcmd}")
	eval "${logcmd1[@]}"
	eval "${logcmd2[@]}"
	eval "${logcmd3[@]}"
done

# flush arp tables
for i in "${interfaces[@]}"
do
	flushcmd=(/sbin/ip neigh flush dev ${i})
	eval ${flushcmd[@]}
done

